package fi.hut.soberit.agilefant.reporting.security;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.ws.rs.core.MultivaluedMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.core.util.MultivaluedMapImpl;

import fi.hut.soberit.agilefant.model.export.User;
import fi.hut.soberit.agilefant.reporting.communication.RemoteCommunicationBuilder;

public class RemoteAuthenticationProviderImpl implements AuthenticationProvider {

  @Autowired
  private RemoteCommunicationBuilder remoteCommunicationBuilder;
  
  public Authentication authenticate(Authentication auth)
      throws AuthenticationException {
    String user = (String) auth.getPrincipal();
    String passwd = (String) auth.getCredentials();
    Client c = remoteCommunicationBuilder.createClient();
    String encoded;
    try {
      encoded = encodePassword(passwd);
    } catch (NoSuchAlgorithmException e1) {
      return null;
    }
    WebResource r = c.resource(this.remoteCommunicationBuilder.getBaseURI() + "/authenticate");
    MultivaluedMap<String, String> m = new MultivaluedMapImpl();
    m.add("username", user);
    m.add("password", encoded);
    User agilefantUser = null;
    try {
      agilefantUser = r.post(User.class, m);
    }
    catch (UniformInterfaceException uie) {
      int status = uie.getResponse().getStatus();
      System.out.println(uie.getMessage());
      if (status == 401) {
        // HTTP authentication to Agilefant's remote API failed
        throw new AuthenticationServiceException("Can't connect to Agilefant's remote API. Try checking the remote.properties file.", uie);
      } else if (status == 403 || status == 412) {
        // Agilefant authentication failed
        throw new BadCredentialsException("Bad username or password");
      } else {
        throw new AuthenticationServiceException("Unknown error occurred when authenticating", uie);
      }
    } catch (Exception e) {
      throw new AuthenticationServiceException("Unknown error occurred", e);
    }
    return new AgilefantAuthenticationToken(user, passwd, agilefantUser);
  }

  private String encodePassword(String passwd)
      throws NoSuchAlgorithmException {
    MessageDigest enc = null;
    enc = MessageDigest.getInstance("MD5");

    enc.update(passwd.getBytes(), 0, passwd.length());
    String encoded = new BigInteger(1, enc.digest()).toString(16);
    return encoded;
  }

  public boolean supports(Class<?> arg0) {
    return true;
  }
}